package com.fishingwithme.android.ui.viewmodels

import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.fishingwithme.android.data.location.LocationService
import com.fishingwithme.android.data.model.Position
import com.fishingwithme.android.data.model.Spot
import com.fishingwithme.android.data.repository.FishingRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.io.File
import java.util.UUID
import javax.inject.Inject
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt

data class SpotHomeUiState(
    val isLoading: Boolean = false,
    val error: String? = null,
    val refreshKey: String? = UUID.randomUUID().toString(),
    val currentLocation: Position? = null,
    val spots: List<Spot> = emptyList(),
    val mapCenter: Position? = null,
    val zoomLevel: Float = 15f,
    val previousMapCenter: Position? = null
)

@HiltViewModel
class SpotsHomeModel @Inject constructor(
    private val repository: FishingRepository,
    private val locationService: LocationService
) : ViewModel() {

    private val _uiState = MutableStateFlow(SpotHomeUiState())
    val uiState: StateFlow<SpotHomeUiState> = _uiState.asStateFlow()

    init {
        loadSpotsByCurrentLocation()
    }

    fun loadSpotsByCurrentLocation() {
        viewModelScope.launch {
            _uiState.update { it.copy(isLoading = true, error = null) }

            try {
                // Get current location
                val locationResult = locationService.getCurrentLocation()
                locationResult.fold(
                    onSuccess = { position ->
                        _uiState.update { it.copy(currentLocation = position) }

                        // Load spots based on current location
                        val spotsResult =
                            repository.loadSpots(position.latitude, position.longitude)
                                .firstOrNull()
                        if (spotsResult != null) {
                            spotsResult.fold(
                                onSuccess = { spots ->
                                    _uiState.update {
                                        it.copy(
                                            isLoading = false,
                                            spots = spots,
                                            error = null,
                                            refreshKey = UUID.randomUUID().toString()
                                        )
                                    }
                                },
                                onFailure = { e ->
                                    _uiState.update {
                                        it.copy(
                                            isLoading = false,
                                            error = "加载钓点失败: ${e.message ?: "未知错误"}",
                                            refreshKey = UUID.randomUUID().toString()
                                        )
                                    }
                                }
                            )
                        } else {
                            _uiState.update {
                                it.copy(
                                    isLoading = false,
                                    error = "未获取到钓点数据",
                                    refreshKey = UUID.randomUUID().toString()
                                )
                            }
                        }
                    },
                    onFailure = { e ->
                        _uiState.update {
                            it.copy(
                                isLoading = false,
                                error = "获取位置失败: ${e.message ?: "未知错误"}",
                                refreshKey = UUID.randomUUID().toString()
                            )
                        }
                    }
                )
            } catch (e: Exception) {
                Log.e("SpotsHomeModel", "Exception loading spots", e)
                _uiState.update {
                    it.copy(
                        isLoading = false,
                        error = "加载钓点时发生错误: ${e.message ?: "未知错误"}",
                        refreshKey = UUID.randomUUID().toString()
                    )
                }
            }
        }
    }

    /**
     * Handle camera change events from the map
     */
     fun onMapCameraChanged(center: Position) {
        _uiState.update {
            it.copy(
                mapCenter = center
            )
        }
    }

    /**
     * Calculate distance between two positions using Haversine formula
     */
    private fun calculateDistance(pos1: Position, pos2: Position): Double {
        val R = 6371e3 // Earth radius in meters
        val lat1Rad = Math.toRadians(pos1.latitude)
        val lat2Rad = Math.toRadians(pos2.latitude)
        val deltaLatRad = Math.toRadians(pos2.latitude - pos1.latitude)
        val deltaLonRad = Math.toRadians(pos2.longitude - pos1.longitude)

        val a = sin(deltaLatRad / 2) * sin(deltaLatRad / 2) +
                cos(lat1Rad) * cos(lat2Rad) *
                sin(deltaLonRad / 2) * sin(deltaLonRad / 2)
        val c = 2 * atan2(sqrt(a), sqrt(1 - a))

        return R * c
    }

    /**
     * Load spots based on the current map center
     */
    fun loadSpotsByMapCenter() {
        val center = _uiState.value.mapCenter ?: return
        
        // Check if we should load new spots based on distance
        // Only skip if we have both current and previous centers and the distance is small
        val previousCenter = _uiState.value.previousMapCenter
        if (previousCenter != null) {
            val distance = calculateDistance(center, previousCenter)
            // Only load new spots if the map has moved more than 100 meters
            if (distance <= 100) {
                return
            }
        }

        viewModelScope.launch {
            _uiState.update { it.copy(isLoading = true, error = null, previousMapCenter = center) }

            try {
                repository.loadSpots(center.latitude, center.longitude).collect { it ->
                    it.fold(
                        onSuccess = { spots ->
                            _uiState.update {
                                it.copy(
                                    isLoading = false,
                                    spots = spots,
                                    error = null,
                                    refreshKey = UUID.randomUUID().toString(),
                                    previousMapCenter = center
                                )
                            }
                        },
                        onFailure = { e ->
                            _uiState.update {
                                it.copy(
                                    isLoading = false,
                                    error = "加载钓点失败: ${e.message ?: "未知错误"}",
                                    refreshKey = UUID.randomUUID().toString()
                                )
                            }
                        }
                    )
                }

            } catch (e: Exception) {
                Log.e("SpotsHomeModel", "Exception loading spots by map center", e)
                _uiState.update {
                    it.copy(
                        isLoading = false,
                        error = "加载钓点时发生错误: ${e.message ?: "未知错误"}",
                        refreshKey = UUID.randomUUID().toString()
                    )
                }
            }
        }
    }

    suspend fun uploadFile(file: File): Flow<Result<String>> {
        return repository.uploadFile(file)
    }

    // 清除错误信息
    fun clearError() {
        _uiState.update { it.copy(error = null) }
    }
}